package pers.lys.aigc4chat.model.baidu;

import com.google.gson.JsonSyntaxException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import pers.hll.aigc4chat.base.constant.AuthType;
import pers.hll.aigc4chat.base.exception.BizException;
import pers.hll.aigc4chat.base.util.BaseUtil;
import pers.hll.aigc4chat.base.util.StringUtil;
import pers.hll.aigc4chat.base.util.XmlUtil;
import pers.hll.aigc4chat.base.xml.BaiduConfig;
import pers.lys.aigc4chat.model.baidu.auth.AuthResp;
import pers.lys.aigc4chat.model.baidu.auth.BceV1Signer;
import pers.lys.aigc4chat.model.baidu.constant.HeaderKey;
import pers.lys.aigc4chat.model.baidu.constant.QueryKey;
import pers.lys.aigc4chat.model.baidu.constant.QueryValue;
import pers.lys.aigc4chat.model.baidu.request.BasePostRequest;

import java.io.IOException;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 百度
 *
 * @author LiYaosheng
 * @author hll
 * @since 2024/3/26
 */
@Slf4j
@UtilityClass
public class BaiduHttpClient {

    public <Q, R> R post(BasePostRequest<Q, R> basePostRequest) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(basePostRequest.getUri());
            addQueries(httpPost);
            // 如果使用AK/SK验证的话 需要添加签字头 所以不要调整 addQueries addHeaders 这两个方法的顺序
            addHeaders(httpPost);
            httpPost.setEntity(new StringEntity(basePostRequest.buildRequestBody(), ContentType.APPLICATION_JSON));
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                return basePostRequest.convertHttpEntityToObj(response.getEntity());
            }
        } catch (IOException | JsonSyntaxException e) {
            log.error("构造Post请求出错: ", e);
            throw BizException.of("构造Post请求出错: ", e);
        }
    }

    /**
     * 更新AccessToken
     */
    public void refreshAccessToken() {
        BaiduConfig baiduConfig = XmlUtil.readXmlConfig(BaiduConfig.class);
        // 鉴权方式为ACCESS_TOKEN
        boolean isAccessTokenAuthType = AuthType.ACCESS_TOKEN == baiduConfig.getAuthType();
        // 第一次鉴权
        boolean isFirstAuth = StringUtil.isNullOrEmpty(baiduConfig.getAccessToken());
        // 鉴权信息是否有效
        boolean isAccessTokenValid = StringUtil.notNullOrEmpty(baiduConfig.getAccessToken())
                && StringUtil.notNullOrEmpty(baiduConfig.getAccessTokenApplyTime())
                && baiduConfig.getAccessTokenApplyTime().plusDays(30).isAfter(LocalDateTime.now());
        if (isAccessTokenAuthType && (isFirstAuth || !isAccessTokenValid)) {
            try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                HttpPost httpPost = new HttpPost(baiduConfig.getAccessTokenUrl());
                List<NameValuePair> params = new ArrayList<>();
                params.add(new BasicNameValuePair(QueryKey.GRANT_TYPE, QueryValue.GRANT_TYPE));
                params.add(new BasicNameValuePair(QueryKey.CLIENT_ID, baiduConfig.getApiKey()));
                params.add(new BasicNameValuePair(QueryKey.CLIENT_SECRET, baiduConfig.getSecretKey()));
                httpPost.setEntity(new UrlEncodedFormEntity(params));
                try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                    HttpEntity entity = response.getEntity();
                    AuthResp authResp = BaseUtil.GSON.fromJson(EntityUtils.toString(entity), AuthResp.class);
                    if (StringUtil.notNullOrEmpty(authResp.getError())) {
                        log.error("获取百度AccessToken失败, 错误码:[{}], 错误描述:[{}]",
                                authResp.getError(), authResp.getErrorDescription());
                        throw BizException.of("获取百度AccessToken失败, 错误码:[{}], 错误描述:[{}]",
                                authResp.getError(), authResp.getErrorDescription());
                    }
                    baiduConfig.setAccessToken(authResp.getAccessToken());
                    baiduConfig.setAccessTokenApplyTime(LocalDateTime.now());
                    XmlUtil.writeXmlConfig(baiduConfig);
                }
            } catch (IOException e) {
                log.error("refreshAccessToken error:", e);
                throw BizException.of("refreshAccessToken error:", e);
            }
        }
    }

    /**
     * 添加请求参数
     *
     * @param httpPost 请求
     */
    private void addQueries(HttpPost httpPost) {
        BaiduConfig baiduConfig = XmlUtil.readXmlConfig(BaiduConfig.class);
        if (AuthType.ACCESS_TOKEN == baiduConfig.getAuthType()) {
            URIBuilder uriBuilder = new URIBuilder(httpPost.getURI());
            uriBuilder.addParameter(QueryKey.ACCESS_TOKEN, baiduConfig.getAccessToken());
            try {
                httpPost.setURI(uriBuilder.build());
            } catch (URISyntaxException e) {
                log.error("构造URI出错: ", e);
                throw BizException.of("构造URI出错: ", e);
            }
        }
    }

    /**
     * 当鉴权类型为{@link AuthType#AK_SK}时，添加请求头 {@code }
     *
     * @param httpRequest 请求
     */
    private void addHeaders(HttpRequestBase httpRequest) {
        BaiduConfig baiduConfig = XmlUtil.readXmlConfig(BaiduConfig.class);
        httpRequest.addHeader(HeaderKey.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());
        if (AuthType.AK_SK == baiduConfig.getAuthType()) {
            BceV1Signer.sign(httpRequest, baiduConfig);
        }
    }
}
